#ifndef __SHM_HPP__
#define __SHM_HPP__

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstdio>
#include <unistd.h>
#include <cstring>

const std::string gpathname = "/home/cp/oslearn/linux_learn/shm";
const int gproj_id = 0x66;
#define gCreater 1
#define gUser 2
#define SIZE 4096 // 建议是4096的整数倍

class Shm
{
private:
    // 通过path和pro_id获取key值
    int GetCommKey()
    {
        key_t key = ftok(_pathname.c_str(), _proj_id);
        if (key < 0)
        {
            perror("ftok");
        }

        return key;
    }

    // 获取共享内存
    int GetShmHeaper(key_t key, size_t size, int flag)
    {
        int shmi = shmget(key, size, flag);
        if (shmi < 0)
        {
            perror("shmget");
        }

        return shmi;
    }

    // 以创建者的身份获取共享内存
    bool GetShmForCreate()
    {
        if (_who == gCreater)
        {
            _shmi = GetShmHeaper(_key, SIZE, IPC_CREAT | IPC_EXCL | 0666);
            std::cout << "shm creater done..." << std::endl;
            sleep(2);
            if (_shmi >= 0)
                return true;
        }
        return false;
    }

    // 以使用者的身份获取共享内存
    bool GetShmForUse()
    {
        if (_who == gUser)
        {
            _shmi = GetShmHeaper(_key, SIZE, IPC_CREAT | 0666);
            sleep(2);

            std::cout << "shm get done..." << std::endl;
            if (_shmi >= 0)
                return true;
        }
        return false;
    }

    std::string GetCurrUsr(int who)
    {
        if (who == gCreater)
            return "gCreater";
        else if (who == gUser)
            return "gUser";
        else
            return "None";
    }

    // 将共享内存挂载到各自的虚拟地址空间、
    void *AttachShm()
    {
        void *addrshm = shmat(_shmi, nullptr, 0);
        if (addrshm == nullptr)
        {
            perror("shmat");
        }
        std::cout << "who: " << GetCurrUsr(_who) << "  attach shm...." << std::endl;
        return addrshm;
    }

    // 取消挂载
    int DetachShm(void *addrshm)
    {
        int res = shmdt(addrshm);
        if (res < 0)
        {
            perror("shmdt");
        }
        std::cout << "who: " << GetCurrUsr(_who) << "  detach shm...." << std::endl;
        return res;
    }

    //
    void *Addrshm()
    {
        return _addrshm;
    }

    // 发送消息一次
    void SendMessageOnce(const std::string &msg)
    {
        void *addrshm = Addrshm();
        memcpy(addrshm, msg.c_str(), msg.size() + 1);
        sleep(1);
    }

public:
    Shm(const std::string &gpathname, int gproj_id, int who)
        : _pathname(gpathname), _proj_id(gproj_id), _who(who)
    {
        _key = GetCommKey();
        if (_who == gCreater)
            GetShmForCreate();
        else
            GetShmForUse();
        _addrshm = AttachShm();
        Clear();

        std::cout << "key: " << _key << std::endl;
        std::cout << "shmi: " << ToHeX() << std::endl;
    }

    ~Shm()
    {
        if (_addrshm != nullptr)
            DetachShm(_addrshm);

        if (_who == gCreater)
        {
            int res = shmctl(_shmi, IPC_RMID, nullptr);
        }
        std::cout << "shm remove done...." << std::endl;
    }

    // 将shmi 转为 16 进制
    std::string ToHeX()
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", _key);
        return buffer;
    }

    // 将共享内存清空
    void Clear()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, SIZE);
        }
    }

    // 发送消息
    void SendMessage(const std::string &msg, int times = -1)
    {
        if (times == -1)
        {
            while (true)
            {
                SendMessageOnce(msg);
            }
        }
        else
        {
            while (times--)
            {
                SendMessageOnce(msg);
            }
        }
    }

    // 接收消息
    void RecerveMessage()
    {
        void *addrshm = Addrshm();
        while (true)
        {
            std::cout << "msg: " << (char *)addrshm << std::endl;
            sleep(1);
        }
    }

private:
    key_t _key; // key值
    int _shmi;  // shmget的返回值，供用户操作

    const std::string _pathname; // 路径
    const int _proj_id;          // pro_id

    int _who;       // 使用共享内存的身份
    void *_addrshm; // 挂载后返回的共享内存的起始地址
};

#endif
